package com.enrollandpay.base;

import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.common.Mapper;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;

import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * 基础接口
 * Select
 * <p>
 * 接口：SelectMapper<T>
 * 方法：List<T> select(T record);
 * 说明：根据实体中的属性值进行查询，查询条件使用等号
 * <p>
 * <p>
 * 接口：SelectByPrimaryKeyMapper<T>
 * 方法：T selectByPrimaryKey(Object key);
 * 说明：根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号
 * <p>
 * <p>
 * 接口：SelectAllMapper<T>
 * 方法：List<T> selectAll();
 * 说明：查询全部结果，select(null)方法能达到同样的效果
 * <p>
 * <p>
 * 接口：SelectOneMapper<T>
 * 方法：T selectOne(T record);
 * 说明：根据实体中的属性进行查询，只能有一个返回值，有多个结果是抛出异常，查询条件使用等号
 * <p>
 * <p>
 * 接口：SelectCountMapper<T>
 * 方法：int selectCount(T record);
 * 说明：根据实体中的属性查询总数，查询条件使用等号
 * <p>
 * <p>
 * Insert
 * <p>
 * 接口：InsertMapper<T>
 * 方法：int insert(T record);
 * 说明：保存一个实体，null的属性也会保存，不会使用数据库默认值
 * <p>
 * <p>
 * 接口：InsertSelectiveMapper<T>
 * 方法：int insertSelective(T record);
 * 说明：保存一个实体，null的属性不会保存，会使用数据库默认值
 * <p>
 * <p>
 * Update
 * <p>
 * 接口：UpdateByPrimaryKeyMapper<T>
 * 方法：int updateByPrimaryKey(T record);
 * 说明：根据主键更新实体全部字段，null值会被更新
 * <p>
 * <p>
 * 接口：UpdateByPrimaryKeySelectiveMapper<T>
 * 方法：int updateByPrimaryKeySelective(T record);
 * 说明：根据主键更新属性不为null的值
 * <p>
 * <p>
 * Delete
 * <p>
 * 接口：DeleteMapper<T>
 * 方法：int delete(T record);
 * 说明：根据实体属性作为条件进行删除，查询条件使用等号
 * <p>
 * <p>
 * 接口：DeleteByPrimaryKeyMapper<T>
 * 方法：int deleteByPrimaryKey(Object key);
 * 说明：根据主键字段进行删除，方法参数必须包含完整的主键属性
 * <p>
 * <p>
 * base组合接口
 * <p>
 * 接口：BaseSelectMapper<T>
 * 方法：包含上面Select的4个方法
 * <p>
 * <p>
 * 接口：BaseInsertMapper<T>
 * 方法：包含上面Insert的2个方法
 * <p>
 * <p>
 * 接口：BaseUpdateMapper<T>
 * 方法：包含上面Update的2个方法
 * <p>
 * <p>
 * 接口：BaseDeleteMapper<T>
 * 方法：包含上面Delete的2个方法
 * <p>
 * <p>
 * CRUD组合接口
 * <p>
 * 接口：BaseMapper<T>
 * 方法：继承了base组合接口中的4个组合接口，包含完整的CRUD方法
 * <p>
 * <p>
 * Example方法
 * 接口：SelectByExampleMapper<T>
 * 方法：List<T> selectByExample(Object example);
 * 说明：根据Example条件进行查询
 * 重点：这个查询支持通过Example类指定查询列，通过selectProperties方法指定查询列
 * <p>
 * <p>
 * 接口：SelectCountByExampleMapper<T>
 * 方法：int selectCountByExample(Object example);
 * 说明：根据Example条件进行查询总数
 * <p>
 * <p>
 * 接口：UpdateByExampleMapper<T>
 * 方法：int updateByExample(@Param("record") T record, @Param("example") Object example);
 * 说明：根据Example条件更新实体record包含的全部属性，null值会被更新
 * <p>
 * <p>
 * 接口：UpdateByExampleSelectiveMapper<T>
 * 方法：int updateByExampleSelective(@Param("record") T record, @Param("example") Object example);
 * 说明：根据Example条件更新实体record包含的不是null的属性值
 * <p>
 * <p>
 * 接口：DeleteByExampleMapper<T>
 * 方法：int deleteByExample(Object example);
 * 说明：根据Example条件删除数据
 * <p>
 * <p>
 * Example组合接口
 * <p>
 * 接口：ExampleMapper<T>
 * 方法：包含上面Example中的5个方法
 * <p>
 * <p>
 * Condition方法
 * Condition方法和Example方法作用完全一样，只是为了避免Example带来的歧义，提供的的Condition方法
 * <p>
 * 接口：SelectByConditionMapper<T>
 * 方法：List<T> selectByCondition(Object condition);
 * 说明：根据Condition条件进行查询
 * <p>
 * <p>
 * 接口：SelectCountByConditionMapper<T>
 * 方法：int selectCountByCondition(Object condition);
 * 说明：根据Condition条件进行查询总数
 * <p>
 * <p>
 * 接口：UpdateByConditionMapper<T>
 * 方法：int updateByCondition(@Param("record") T record, @Param("example") Object condition);
 * 说明：根据Condition条件更新实体record包含的全部属性，null值会被更新
 * <p>
 * <p>
 * 接口：UpdateByConditionSelectiveMapper<T>
 * 方法：int updateByConditionSelective(@Param("record") T record, @Param("example") Object condition);
 * 说明：根据Condition条件更新实体record包含的不是null的属性值
 * <p>
 * <p>
 * 接口：DeleteByConditionMapper<T>
 * 方法：int deleteByCondition(Object condition);
 * 说明：根据Condition条件删除数据
 * <p>
 * <p>
 * Condition组合接口
 * <p>
 * 接口：ConditionMapper<T>
 * 方法：包含上面Condition中的5个方法
 * <p>
 * <p>
 * RowBounds
 * 默认为内存分页，可以配合PageHelper实现物理分页
 * <p>
 * 接口：SelectRowBoundsMapper<T>
 * 方法：List<T> selectByRowBounds(T record, RowBounds rowBounds);
 * 说明：根据实体属性和RowBounds进行分页查询
 * <p>
 * <p>
 * 接口：SelectByExampleRowBoundsMapper<T>
 * 方法：List<T> selectByExampleAndRowBounds(Object example, RowBounds rowBounds);
 * 说明：根据example条件和RowBounds进行分页查询
 * <p>
 * <p>
 * 接口：SelectByConditionRowBoundsMapper<T>
 * 方法：List<T> selectByConditionAndRowBounds(Object condition, RowBounds rowBounds);
 * 说明：根据example条件和RowBounds进行分页查询，该方法和selectByExampleAndRowBounds完全一样，只是名字改成了Condition
 * <p>
 * <p>
 * RowBounds组合接口
 * <p>
 * 接口：RowBoundsMapper<T>
 * 方法：包含上面RowBounds中的前两个方法，不包含selectByConditionAndRowBounds
 * <p>
 * <p>
 * special特殊接口
 * 这些接口针对部分数据库设计，不是所有数据库都支持
 * <p>
 * 接口：InsertListMapper<T>
 * 方法：int insertList(List<T> recordList);
 * 说明：批量插入，支持批量插入的数据库可以使用，例如MySQL,H2等，另外该接口限制实体包含id属性并且必须为自增列
 * <p>
 * <p>
 * 接口：InsertUseGeneratedKeysMapper<T>
 * 方法：int insertUseGeneratedKeys(T record);
 * 说明：插入数据，限制为实体包含id属性并且必须为自增列，实体配置的主键策略无效
 * <p>
 * <p>
 * Created by Administrator on 2017/5/9 0009.
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class MBaseService<T, M extends Mapper<T>>
{

    protected Class<T> entityClass;

    public MBaseService()
    {
        if (this.entityClass == null)
        {
            this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
    }

    @Autowired
    private M myBatisDao;

    public M getMyBatisDao()
    {
        return myBatisDao;
    }

    public void setMyBatisDao(M myBatisDao)
    {
        this.myBatisDao = myBatisDao;
    }


    /*
    public int insert(T t) throws Exception{
        Set<EntityColumn> entityColumns = EntityHelper.getPKColumns(this.entityClass);
        Iterator<EntityColumn> it = entityColumns.iterator();
        EntityColumn entityColumn = null;
        while (it.hasNext()) {
            entityColumn = it.next();
            if(entityColumn.isId() && !entityColumn.isBusinessid()){
                break;
            }
        }
        //当前台传入了ID,如果有值,直接insert
        String id = BeanUtils.getProperty(t, entityColumn.getProperty());
        if(StringUtils.isBlank(id)){
            if(entityColumn.isUuid()){
                BeanUtils.setProperty(t, entityColumn.getProperty(), UUID.randomUUID().toString().replaceAll("-", ""));
            }else if("NO".equals(StringUtils.upperCase(entityColumn.getGenerator()))){
                BeanUtils.setProperty(t, entityColumn.getProperty(), VeDate.getNo(6));
            }
        }

        return this.myBatisDao.insert(t);
    }
    */


    /**
     * 根据实体中的属性值进行查询，查询条件使用等号
     * select(null) 等同于selectAll()
     *
     * @param record 查询条件封装的实体类
     * @return List<T> 符合查询条件的实体集合
     */
    public List<T> select(T record)
    {
        return myBatisDao.select(record);
    }


    /**
     * 查询全部
     *
     * @return
     */
    public List<T> selectAll()
    {
        return myBatisDao.selectAll();
    }

    /**
     * 根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号
     *
     * @param key 主键
     * @return 符合查询条件的数据
     */
    public T selectByPrimaryKey(Object key)
    {
        return myBatisDao.selectByPrimaryKey(key);
    }


    /**
     * 根据实体中的属性进行查询，只能有一个返回值，有多个结果是抛出异常，查询条件使用等号
     *
     * @param record 查询条件封装的实体类
     * @return
     */
    public T selectOne(T record)
    {
        return myBatisDao.selectOne(record);
    }

    /**
     * 根据实体中的属性查询总数
     *
     * @param record 实体类
     * @return
     */
    public int selectCount(T record)
    {
        return myBatisDao.selectCount(record);
    }

    /**
     * 单表分页查询所有数据
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    public List<T> selectPage(int pageNum, int pageSize)
    {
        PageHelper.startPage(pageNum, pageSize);
        //Spring4支持泛型注入
        return myBatisDao.select(null);
    }

    /**
     * 根据example进行条件查询
     *
     * @param example 带有查询条件的Example实例
     * @return 符合查询条件的集合
     */
    public List<T> selectByExample(Object example)
    {
        return myBatisDao.selectByExample(example);
    }


    /**
     * 保存一个实体，null的属性也会保存，不会使用数据库默认值
     *
     * @param record
     * @return
     */
    @Transactional
    public int insert(T record)
    {
        return myBatisDao.insert(record);
    }

    /**
     * 保存一个实体，null的属性不会保存，会使用数据库默认值
     *
     * @param record
     * @return
     */
    public int insertSelective(T record)
    {
        return myBatisDao.insertSelective(record);
    }

    /**
     * 根据主键更新实体全部字段，null值会被更新
     *
     * @param record
     * @return
     */
    @Transactional
    public int updateByPrimary(T record)
    {
        return myBatisDao.updateByPrimaryKey(record);
    }

    /**
     * 根据主键更新属性不为null的值
     *
     * @param record record除了主键之外，必须要有一个不为null的字段
     * @return
     */
    public int updateByPrimarySelective(T record)
    {
        return myBatisDao.updateByPrimaryKeySelective(record);
    }


    @Transactional
    public int delete(T record)
    {
        return myBatisDao.delete(record);
    }

    /**
     * 根据主键删除
     *
     * @param key
     * @return
     */
    @Transactional
    public int deleteByPrimaryKey(Object key)
    {
        return myBatisDao.deleteByPrimaryKey(key);
    }

}
